package SPAN::Model::Multi::Method;

use warnings;
use strict;

use Reaction::Class;

use Reaction::Types::Core;
use SPAN::Types::Math::Calculus;

use Benchmark ();

class Method is 'Reaction::InterfaceModel::Action', which {

    has expression => (
        isa => 'Expression',
        is  => 'rw',
        set_or_lazy_fail('expression')
    );
    has equiv_exp =>
        ( isa => 'Expression', is => 'rw', set_or_lazy_fail('equiv_exp') );
    has variable =>
        ( isa => 'Variable', is => 'rw', set_or_lazy_fail('variable') );
    has start => ( isa => 'Real', is => 'rw', set_or_lazy_fail('start') );
    has end   => ( isa => 'Real', is => 'rw', set_or_lazy_fail('end') );
    has precision =>
        ( isa => 'PositiveInt', is => 'rw', set_or_lazy_fail('precision') );
    has xzero => ( isa => 'Real', is => 'rw', set_or_lazy_fail('xzero') );
    has xone  => ( isa => 'Real', is => 'rw', set_or_lazy_fail('xtwo') );

    implements do_apply => as {
        my ($self) = @_;
        my $methods = $self->target_model->{methods};
        my @results;
        foreach my $moniker ( keys %$methods ) {
            my $class = $methods->{$moniker}{class};
            my %params = map { my $name = $_->name; $name => $self->$name }
                $class->parameter_attributes;
            my $backend = $class->backend;
            my $method = eval { $backend->new(%params); };
            if ($@) {
                my $error = $@;
                die $error unless ref($error);
                return { error => $$error, params => \%params };
            }
            my $value = $method->run;
            my $benchmark;
            {
                my $curr = select;
                select STDERR;
                no warnings 'uninitialized';
                $benchmark = Benchmark::timethis( 300,
                    sub { $backend->new(%params)->run } );
                select $curr;
            }
            my %data = (
                value      => $value,
                params     => \%params,
                total_time => $benchmark->timestr
            );

            if ( my $call = $method->can('partial_results') ) {
                $data{partial_results} = $call->($method);
            }
            $data{method} = $methods->{$moniker};
            push @results, \%data;
        }
        return \@results;
    };
};

1;
